package com.fengye.common.aspectj;

import com.fengye.common.annotation.RequestLimit;
import com.fengye.common.utils.RedisUtils;
import com.fengye.common.constants.AppHttpCodeEnum;
import com.fengye.common.exception.BusinessException;
import com.fengye.common.utils.TokenExtractorUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author sunlei
 * @version 1.0
 * @date 2024/5/10 14:45:59
 * @description 防止重复提交请求aop实现类，常用于 防止表单重复提交、防止重复获取验证码、防止重复创建订单、防止重复支付等等场景
 */
@Slf4j
@Aspect
@Component
public class RequestLimitAspect {

    @Autowired
    private RedisUtils redisUtils;
    @Around("@annotation(com.fengye.common.annotation.RequestLimit)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //拿NoRepeatSubmit的注解
        RequestLimit requestLimit = method.getAnnotation(RequestLimit.class);
        String lockKey = getLockKey();

        if (redisUtils.exists(lockKey)) {
            int count = (Integer) redisUtils.get(lockKey);
            log.info("统计请求次数1：key:{},count:{}", lockKey, count);
            // 如果redis缓存中有这个key则视为重复提交
            if (requestLimit.maxCount() < count + 1) {
                throw new BusinessException(AppHttpCodeEnum.TOO_MANY_SAMPLE_REQUESTS);
            }
            redisUtils.increment(lockKey, 1);
        } else {
            Long incremented = redisUtils.increment(lockKey, 1);
            redisUtils.expire(lockKey, requestLimit.interval(), requestLimit.intervalTimeUnit());
            log.info("统计请求次数2：key:{},count:{}", lockKey, incremented);
        }

        return joinPoint.proceed();
    }

    private String getLockKey() {
        // 获取请求对象
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        // 获取请求的token
        String token = TokenExtractorUtils.getHeaderBearerToken(request);

        if (StringUtils.isBlank(token)) {
            throw new BusinessException(AppHttpCodeEnum.UNAUTHORIZED);
        }

        // 获取请求的url
        String path = request.getRequestURI();

        return token + path;
    }
}
